#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <assert.h>
#include <time.h>
#include "multi_pattern_ac_nfa.h"

static FILE *out_ptr;

typedef struct list_node_s {
    void *id;
    struct list_node_s *prev, *next;
} list_node_t;

/*
 *  Text Data Buffer
 */
int MatchFound(void *id, void *in, int index, void *data)
{
//    fprintf(stdout, "%s\n", (char *) id);
	list_node_t *ln;
	for (ln = in; ln != NULL && ln->id != NULL; ln = ln->next)
	    fprintf(out_ptr, "%d\n", (int)ln->id);
	fprintf(out_ptr, "\n");
    return 0;
}

int build(void *id, void **existing_tree)
{
	list_node_t *new_node;

	new_node = malloc(sizeof(list_node_t));
    
	new_node->id = id;
	new_node->next = NULL;
	new_node->prev = NULL;

	if ((*existing_tree) == NULL) {
	    new_node->prev = new_node;
	    (*existing_tree) = new_node;
	} else {
	    ((list_node_t *)(*existing_tree))->prev->next = new_node;
	    new_node->prev = ((list_node_t *)(*existing_tree))->prev;
	    ((list_node_t *)(*existing_tree))->prev = new_node;
	}
	return 1;
}

int get_hex(char ch)
{
		if (ch >= '0' && ch <= '9')
				return ch - '0';
		else if (ch >= 'A' && ch <= 'F')
				return ch - 'A' + 10;
		else if (ch >= 'a' && ch <= 'f')
				return ch - 'a' + 10;
		else
				assert(0);
				
}

void convert(char *in, char *out, int *ret)
{
		int len, i;

		len = strlen(in);
		assert(len % 2 == 0);
		len /= 2;
	
//		printf("%s\n", in);	
		for (i = 0; i < len; i++) {
				out[i] = get_hex(in[2 * i]) * 16 + get_hex(in[2 * i + 1]);
		}		
		out[i] = '\0';
//		printf("%s\n", out);
		*ret = len;
		return;
}

void test_algorithm2(char *pattern_in, char *data_in, char *out_name)
{
	char str[4001];	
	char pattern[1000];
	char text[2001];
	int  rid, len, nocase, current_state;
	FILE *pat_file, *data_file;
	ac_t *acsm;
	unsigned int start, end;
	unsigned int compile_time, search_time;
	int pattern_count, search_count;

	pat_file = fopen(pattern_in, "r");
	assert(pat_file != NULL);
	data_file = fopen(data_in, "r");
	assert(data_file != NULL);
	out_ptr = fopen(out_name, "w");
	assert(out_ptr != NULL);

	printf("Test algorithm : AC-NFA\n");
	acsm = ac_new(NULL);
	nocase = 0;
	compile_time = 0;
	pattern_count = 0;
	while (fscanf(pat_file, "%s%d", str, &rid) != EOF) {
			pattern_count++;
			convert(str, pattern, &len);			
			ac_add_pattern(acsm, pattern, len, nocase, (void *)rid);			
	}
	fclose(pat_file);
//	printf("pattern in\n");

	start = clock();
	ac_compile(acsm, build);
	end = clock();
	compile_time = end - start;
//	printf("pattern compile\n");

	search_time = 0;
	search_count = 0;
	while (fscanf(data_file, "%s", str) != EOF) {
			search_count++;
			convert(str, text, &len);
			current_state = 0;
			start = clock();
			ac_search(acsm, text, len, MatchFound, (void *)0, &current_state);
			end = clock();
			search_time += end - start;
	}
//	printf("search end\n");
	
	printf("pattern_count   = %d\n", pattern_count);
	printf("search_count    = %d\n", search_count);
	printf("compile_time    = %u (ms)\n", compile_time);
	printf("tot search time = %u (ms)\n", search_time);
	printf("avg search time = %u (us)\n", search_time * 1000/ search_count);
	
	fclose(data_file);
	fclose(out_ptr);
	ac_free(acsm);

	return;
}

void test_case0()
{
		printf("Test case 0:\n");
		test_algorithm2("test0/pattern.txt", "test0/test0.in", "test0/test0_1.out");
		printf("\n");
		printf("Test case 1:\n");
		test_algorithm2("test1/pattern.txt", "test1/test1.in", "test1/test1_1.out");
		printf("\n");
		printf("Test case 2:\n");
		test_algorithm2("test2/pattern.txt", "test2/test2.in", "test2/test2_1.out");
		printf("\n");
		printf("Test case 3:\n");
		test_algorithm2("test3/pattern.txt", "test3/test3.in", "test3/test3_1.out");
		printf("\n");
		printf("Test case 4:\n");
		test_algorithm2("test4/pattern.txt", "test4/test4.in", "test4/test4_1.out");
		printf("\n");	
		printf("Test case 5:\n");
		test_algorithm2("test5/pattern.txt", "test5/test5.in", "test5/test5_1.out");
		printf("\n");		
}
/*
 *
 */
int main(int argc, char **argv)
{
		test_case0();
	    return (0);
}
